﻿#include "oeasyproxy.h"
#ifndef NO_WIDGETS
#include "ui_oeasyproxy.h"
#endif
#include "HCNetSDK.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include "logfile.h"
#include "configfile.h"
#include "commonfuc.h"
#include "http.h"

void CALLBACK fExceptionCB(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
    DEBUG_PRINT << "";
    STRUCT_IPC_NVR_LIST *temp=(STRUCT_IPC_NVR_LIST *)pUser;
    QList<IPC*> *p = (QList<IPC *> *)temp->ipcList;
    IPC *ipc = Q_NULLPTR;
    QList<NVR*> *n = (QList<NVR *> *)temp->nvrList;
    NVR *nvr = Q_NULLPTR;
    QString log;
    foreach (IPC *tmp, *p)
    {
        if (tmp != Q_NULLPTR && tmp->m_lUserID == lUserID)
        {
            ipc = tmp;
        }
    }

    if (ipc == Q_NULLPTR)//在ipc列表中没找到则往nvr中找
    {
        foreach (NVR *tmp, *n)
        {
            if (tmp != Q_NULLPTR && tmp->m_lUserID == lUserID)
            {
                nvr = tmp;
            }
        }

        if (nvr == Q_NULLPTR)//没在nvr中找到
        {
            log.clear();
            log.sprintf("not find nvr: lUserID = %d, lHandle = %d, dwType = 0x%x", lUserID, lHandle, (int)dwType);
            logFile::printLog(log);
            return ;
        }
        else//在nvr中找到
        {
            log.clear();
            log.sprintf("find nvr match: lUserID = %d, lHandle = %d, dwType = 0x%x", lUserID, lHandle, (int)dwType);
            logFile::printLog(log);
            switch (dwType)
            {
            case EXCEPTION_EXCHANGE:
                break;
            case EXCEPTION_RECONNECT:
                nvr->m_devState = false;
                break;
            case EXCEPTION_ALARMRECONNECT:
                break;
            case PREVIEW_RECONNECTSUCCESS:
                nvr->m_devState = true;
                break;
            case ALARM_RECONNECTSUCCESS:
                break;
            case RESUME_EXCHANGE:
                break;
            default:
                log.clear();
                log.sprintf("default: dwType = 0x%x",(int)dwType);
                logFile::printLog(log);
                break;
            }
        }
    }
    else//在ipc中找到
    {
        log.clear();
        log.sprintf("find ipc match: ip = %s, lHandle = %d, dwType = 0x%x", ipc->m_ipcInfo.ip.toStdString().c_str(), lHandle, (int)dwType);
        logFile::printLog(log);
        switch (dwType)
        {
        case EXCEPTION_EXCHANGE:
            break;
        case EXCEPTION_RECONNECT:
            ipc->m_devState = false;
            break;
        case EXCEPTION_ALARMRECONNECT:
            break;
        case PREVIEW_RECONNECTSUCCESS:
            ipc->m_devState = true;
            break;
        case ALARM_RECONNECTSUCCESS:
            break;
        case RESUME_EXCHANGE:
            break;
        default:
            log.clear();
            log.sprintf("default: dwType = 0x%x",(int)dwType);
            logFile::printLog(log);
            break;
        }
    }
}

void CALLBACK MessageCallback(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void *pUser)
{
    QString log;
    switch (lCommand)
    {
    case COMM_ALARM_V30:
    {
        STRUCT_IPC_NVR_LIST *temp=(STRUCT_IPC_NVR_LIST *)pUser;
        QList<IPC*> *p = (QList<IPC *> *)temp->ipcList;

        NET_DVR_ALARMINFO_V30 struAlarmInfo;
        memcpy(&struAlarmInfo, pAlarmInfo, dwBufLen);

        switch (struAlarmInfo.dwAlarmType)
        {
        case 3: //移动侦测报警
            foreach (IPC *ipc, *p)
            {
                if (ipc != Q_NULLPTR && ipc->m_lUserID == pAlarmer->lUserID)
                {
                    emit ipc->motionAlarm();
                }
            }
            break;
        default:
            log.clear();
            log.sprintf("missing: device ip: %s ,dwAlarmType = %d",pAlarmer->sDeviceIP, struAlarmInfo.dwAlarmType);
            logFile::printLog(log);
            break;
        }
    }
        break;
    default:
        log = "missing lCommand";
        logFile::printLog(log);
        break;
    }
}

#ifndef NO_WIDGETS
OeasyProxy::OeasyProxy(QWidget *parent) :
    QMainWindow(parent),
#else
OeasyProxy::OeasyProxy(QObject *parent) :
    QObject(parent),
#endif
    m_cfg(),
    m_ipcList(),
    m_nvrList(),
    m_picServer(m_cfg.m_cfg.general.faceJpgRecvPort, m_cfg.m_cfg.general.proxyId + "_aaaa_1", m_cfg.m_cfg.face),
    m_intercom(m_cfg.m_cfg.general.intercomPort),
    m_access(m_ipcList, m_cfg.m_cfg.general.proxyUrl, m_cfg.m_cfg.general.proxyId, m_cfg.m_cfg.face.enterUrl),
    m_targetCli(m_cfg.m_cfg.general.proxyId, m_cfg.m_cfg.target, m_ipcList),
    m_picManager(),
    m_reportManager(),
    m_bigDataManager(),
    m_postMap(),
    m_timer()
{
#ifndef NO_WIDGETS
    ui = new Ui::OeasyProxy;
    ui->setupUi(this);
#endif

     m_cfg.SetConfig(m_cfg.m_cfg);
     logFile::g_pConfig = &m_cfg;
     QString log;
     log.sprintf("**************Program startup**********\n");
     logFile::printLog(log);

#ifdef UPLOAD_VIDEOANDPICS_TO_QINIU
    /* 全局初始化函数，整个进程只需要调用一次 */
    Qiniu_Global_Init(-1);
#endif

#ifdef UPLOAD_VIDEOANDPICS_TO_QINIU
    m_upload.setTransferInfo(m_cfg.m_cfg.transferInfo);
    m_upload.setIsUseNvr(m_cfg.m_cfg.bUseNvr);
#endif

	m_ipc_nvrList.ipcList=(void *)&m_ipcList;
    m_ipc_nvrList.nvrList=(void *)&m_nvrList;

#ifdef USE_NVR_GETVIDEOANDPICS
    createRecordFolder();
#endif

    initIpc();
    initLog();

    m_picManager.setCookieJar(Q_NULLPTR);
    m_reportManager.setCookieJar(Q_NULLPTR);
    m_bigDataManager.setCookieJar(Q_NULLPTR);
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
    connect(&m_picManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onPostFinish(QNetworkReply*)));
    connect(&m_reportManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onPostFinish(QNetworkReply*)));
    connect(&m_bigDataManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onPostFinish(QNetworkReply*)));

    connect(&m_intercom, SIGNAL(sigOpenDoor(QByteArray,QString)), this, SLOT(onOpenDoor(QByteArray,QString)));

    connect(&m_targetCli, SIGNAL(postPic(QByteArray)), this, SLOT(onPostPic(QByteArray)));
    connect(&m_targetCli, SIGNAL(postReport(QByteArray)), this, SLOT(onPostReport(QByteArray)));

#ifdef UPLOAD_VIDEOANDPICS_TO_QINIU
    int nSize = -1;
    if(m_cfg.m_cfg.bUseNvr)
    {
        NVR* pNvr = NULL;
        nSize = m_nvrList.size();
        for (int i = 0; i < nSize; i++)
        {
            pNvr = m_nvrList.at(i);
            if(NULL != pNvr)
            {
                connect(pNvr, SIGNAL(snapAndGetVideoOK(QByteArray, QString, QString, QString, QString)),&m_upload, SLOT(snapAndGetVideoOK_Slot(QByteArray, QString, QString, QString, QString)));
            }
        }
    }
    else
    {
        IPC* pIPC = NULL;
        nSize = m_ipcList.size();
        for (int i = 0; i < nSize; i++)
        {
            pIPC = m_ipcList.at(i);
            if(NULL != pIPC)
            {
                connect(pIPC, SIGNAL(snapAndGetVideoOK(QByteArray, QString, QString, QString, QString)),&m_upload, SLOT(snapAndGetVideoOK_Slot(QByteArray, QString, QString, QString, QString)));
            }
        }
    }
#endif
    m_timer.start(m_cfg.m_cfg.general.postTimeout);
}

OeasyProxy::~OeasyProxy()
{
    foreach (IPC *ipc, m_ipcList)
    {
        if (ipc != Q_NULLPTR)
        {
            m_ipcList.removeOne(ipc);
            delete ipc;
        }
    }
#ifdef USE_NVR_GETVIDEOANDPICS
    foreach (NVR *nvr, m_nvrList)
    {
        if (nvr != Q_NULLPTR)
        {
            m_nvrList.removeOne(nvr);
            delete nvr;
        }
    }
    // 释放SDK资源
    NET_DVR_Cleanup();
#endif
    /* 全局清理函数，只需要在进程退出时调用一次 */
#ifdef UPLOAD_VIDEOANDPICS_TO_QINIU
    Qiniu_Global_Cleanup();
#endif

    QString log;
    log.sprintf("**************Program is over**************\n\n");
    logFile::printLog(log);

#ifndef NO_WIDGETS
    delete ui;
#endif

}

void OeasyProxy::createRecordFolder()
{
   QString strRoot = CommonFuc::getAppDirPath();
   QString strPath;
   strPath.append(strRoot);
   strPath.append("record");
   QDir nvrDir;
   if (!nvrDir.exists(strPath))
   {
       nvrDir.mkdir(strPath);
   }

   int i=0;
   QStringList fileStrList;
   nvrDir.setPath(strPath);
   fileStrList=nvrDir.entryList();
   for(i=0;i<fileStrList.size();i++)
   {
       if(0==fileStrList.at(i).compare(".")||0==fileStrList.at(i).compare(".."))
           continue;
       QString fileName;
       fileName.append(strRoot);
       fileName.append("record/");
       fileName.append(fileStrList.at(i));
       QFile::remove(fileName);
   }
}

void OeasyProxy::initIpc()
{
    BOOL bRet;
    QString log;
    DEBUG_PRINT << "initIpc...";
    //SDK初始化
    bRet = NET_DVR_Init();
    if (!bRet)
    {
        log.clear();
        log.sprintf("NET_DVR_Init error: %d", NET_DVR_GetLastError());
        logFile::printLog(log);
        WARNING_PRINT << "NET_DVR_Init error:" << NET_DVR_GetLastError();
    }
    //注册异常消息回调函数
//    bRet = NET_DVR_SetExceptionCallBack_V30(WM_NULL, NULL, fExceptionCB, (void*)&m_ipcList);
 #ifndef WIN32
 #define WM_NULL 0x0000  //sd copy from winuser.h
 #endif
	bRet = NET_DVR_SetExceptionCallBack_V30(WM_NULL, NULL, fExceptionCB, (void*)&m_ipc_nvrList);
    if (!bRet)
    {
        log.clear();
        log.sprintf("NET_DVR_SetExceptionCallBack_V30 error: %d", NET_DVR_GetLastError());
        logFile::printLog(log);
        WARNING_PRINT << "NET_DVR_SetExceptionCallBack_V30 error:" << NET_DVR_GetLastError();
    }
    //设置报警回调函数
//    bRet = NET_DVR_SetDVRMessageCallBack_V30(MessageCallback, (void*)&m_ipcList);
	bRet = NET_DVR_SetDVRMessageCallBack_V30(MessageCallback, (void*)&m_ipc_nvrList);
    if (!bRet)
    {
        log.clear();
        log.sprintf("NET_DVR_SetDVRMessageCallBack_V30 error: %d", NET_DVR_GetLastError());
        logFile::printLog(log);
        WARNING_PRINT << "NET_DVR_SetDVRMessageCallBack_V30 error:" << NET_DVR_GetLastError();
    }

    for (int i = 0, j = ceil(sqrt(m_cfg.m_cfg.ipcList.size())); i < m_cfg.m_cfg.ipcList.size(); i++)
    {
#ifndef NO_WIDGETS
        QWidget *widget = new QWidget(ui->tab_video);
        ui->gridLayout_video->addWidget(widget, i/j, i%j);      
        m_ipcList.append(new IPC(m_cfg.m_cfg.general.proxyId, QString("").sprintf("%04d", i), m_cfg.m_cfg.ipcList[i], m_cfg.m_cfg.face, m_cfg.m_cfg.car, widget));
#else
        m_ipcList.append(new IPC(m_cfg.m_cfg.general.proxyId, QString("").sprintf("%04d", i), m_cfg.m_cfg.ipcList[i], m_cfg.m_cfg.face, m_cfg.m_cfg.car));
#endif
        connect(m_ipcList.at(i), SIGNAL(faceSuccess(QString, QString)), &m_intercom, SLOT(onFaceOpenDoor(QString, QString)));
        connect(m_ipcList.at(i), SIGNAL(postPic(QByteArray)), this, SLOT(onPostPic(QByteArray)));
        connect(m_ipcList.at(i), SIGNAL(postReport(QByteArray)), this, SLOT(onPostReport(QByteArray)));
        connect(m_ipcList.at(i), SIGNAL(postBigData(QByteArray)), this, SLOT(onPostBigData(QByteArray)));
    }
#ifdef USE_NVR_GETVIDEOANDPICS
    if(m_cfg.m_cfg.bUseNvr)
    {
        for (int i = 0; i < m_cfg.m_cfg.nvrList.size(); i++)
        {
            m_nvrList.append(new NVR(m_cfg.m_cfg.nvrList[i]));
        }
    }
#endif
}

void OeasyProxy::initLog()
{
#ifndef NO_WIDGETS
    QTextEdit *log_access = new QTextEdit(ui->tab_log);
    log_access->hide();
    ui->gridLayout_log->addWidget(log_access, 0, 0);
    connect(log_access, SIGNAL(textChanged()), this, SLOT(onLogChanged()));
    connect(&m_access, SIGNAL(textRecv(QString)), log_access, SLOT(append(QString)));
    connect(&m_access, SIGNAL(textRecv(QString)), log_access, SLOT(show()));

    QTextEdit *log_targetCli = new QTextEdit(ui->tab_log);
    log_targetCli->hide();
    ui->gridLayout_log->addWidget(log_targetCli, 0, 1);
    connect(log_targetCli, SIGNAL(textChanged()), this, SLOT(onLogChanged()));
    connect(&m_targetCli, SIGNAL(textRecv(QString)), log_targetCli, SLOT(append(QString)));
    connect(&m_targetCli, SIGNAL(textRecv(QString)), log_targetCli, SLOT(show()));

    QTextEdit *log_intercom = new QTextEdit(ui->tab_log);
    log_intercom->hide();
    ui->gridLayout_log->addWidget(log_intercom, 1, 0);
    connect(log_intercom, SIGNAL(textChanged()), this, SLOT(onLogChanged()));
    connect(&m_intercom, SIGNAL(textRecv(QString)), log_intercom, SLOT(append(QString)));
    connect(&m_intercom, SIGNAL(textRecv(QString)), log_intercom, SLOT(show()));

    QTextEdit *log_picServer = new QTextEdit(ui->tab_log);
    log_picServer->hide();
    ui->gridLayout_log->addWidget(log_picServer, 1, 1);
    connect(log_picServer, SIGNAL(textChanged()), this, SLOT(onLogChanged()));
    connect(&m_picServer, SIGNAL(textRecv(QString)), log_picServer, SLOT(append(QString)));
    connect(&m_picServer, SIGNAL(textRecv(QString)), log_picServer, SLOT(show()));
#endif
}

void OeasyProxy::post(QNetworkAccessManager &m, QNetworkRequest req, const QPair<QByteArray, int> &data)
{
    m.clearAccessCache();
    QNetworkReply *r = m.post(req, data.first);
    if (r != Q_NULLPTR)
    {
        m_postMap[r] = data;
        connect(r, SIGNAL(readyRead()), this, SLOT(onPostReadyRead()));
    }
}

void OeasyProxy::onLogChanged()
{
#ifndef NO_WIDGETS
    QTextEdit *tmp = (QTextEdit*)this->sender();
    QString textContent = tmp->toPlainText();
    int len = textContent.count();
    int maxLen = 1024;
    if (len > maxLen)
    {
        textContent.remove(0, len - maxLen);
        tmp->setText(textContent);

        QTextCursor textCursor = tmp->textCursor();
        textCursor.movePosition(QTextCursor::End);
        tmp->setTextCursor(textCursor);
    }
#endif
}

void OeasyProxy::onPostPic(const QByteArray &data)
{
    QNetworkRequest req(QUrl(m_cfg.m_cfg.general.picUrl + "/upload"));

    req.setHeader(QNetworkRequest::ContentTypeHeader, "image/jpeg");
    post(m_picManager, req, qMakePair(data, -1));
}

void OeasyProxy::onPostReport(const QByteArray &data)
{
    QNetworkRequest req(QUrl(m_cfg.m_cfg.general.reportUrl));

    req.setHeader(QNetworkRequest::ContentTypeHeader, "json");
    post(m_reportManager, req, qMakePair(data, -1));
}

void OeasyProxy::onPostBigData(const QByteArray &data)
{
    QString log;
    QNetworkRequest req(QUrl(m_cfg.m_cfg.general.bigDataUrl + "?msgJson=" + data));

    req.setHeader(QNetworkRequest::ContentTypeHeader, "json");
    m_bigDataManager.clearAccessCache();
    if (m_bigDataManager.children().size() > 16)
    {
        log.clear();
        log.sprintf("too many connect: %d", m_bigDataManager.children().size());
        logFile::printLog(log);
        WARNING_PRINT << req.url() << "too many connect" << m_bigDataManager.children().size();
        m_bigDataManager.children().first()->deleteLater();
    }
    QNetworkReply *r = m_bigDataManager.get(req);
    if (r != Q_NULLPTR)
    {
        connect(r, SIGNAL(readyRead()), this, SLOT(onPostReadyRead()));
    }
}

void OeasyProxy::onPostReadyRead()
{
    QNetworkReply *r = (QNetworkReply *)this->sender();
    QByteArray data = r->readAll();
    Q_UNUSED(data);
}

void OeasyProxy::onPostFinish(QNetworkReply *r)
{
    if (r->error() != QNetworkReply::NoError)
    {
        WARNING_PRINT << r->url() << r->error();
    }
    r->deleteLater();
    m_postMap.remove(r);
}

void OeasyProxy::onTimeout()
{
    QList<QNetworkReply *> keys = m_postMap.keys();
    foreach (QNetworkReply *r, keys)
    {
        QPair<QByteArray, int> &data = m_postMap[r];
        data.second++;
        if (data.second >= 1 && data.second <= m_cfg.m_cfg.general.postFailNum)
        {
            post(*r->manager(), r->request(), m_postMap[r]);
            DEBUG_PRINT << r->url() << r->bytesToWrite() << data.second << data.first.size();
            m_postMap.remove(r);
            r->deleteLater();
        }
        else if (data.second > m_cfg.m_cfg.general.postFailNum + 1)
        {
            QFile file(QString("").sprintf("%p.jpg",r));
            if (!file.exists())
            {
                if (file.open(QIODevice::WriteOnly))
                {
                    file.write(data.first);
                    file.close();
                }
            }
            m_postMap.remove(r);
            r->deleteLater();
        }
    }
}

void OeasyProxy::onOpenDoor(QByteArray data,QString mac)
{
#ifdef USE_NVR_GETVIDEOANDPICS
    QString log;
    int i;
    for(i=0;i<m_cfg.m_cfg.ipcList.size();i++)
    {
        if(0==QString::compare(mac,m_cfg.m_cfg.ipcList.at(i).intercomMac))
            break;
    }
    if(i>=m_cfg.m_cfg.ipcList.size())//没找到
    {
        log.clear();
        log.sprintf("not find mac in ipcList,mac= %s", mac.toStdString().c_str());
        logFile::printLog(log);
        WARNING_PRINT << "not find mac in ipcList,mac=" <<mac;
        return;
    }
    if(m_cfg.m_cfg.bUseNvr)//使用nvr
    {
        int nvrNo = m_cfg.m_cfg.ipcList.at(i).parentNVR;
        if(nvrNo<m_nvrList.size()&&nvrNo>=0)
        {
            NVR *nvr=m_nvrList.at(nvrNo);
            emit nvr->sigSnapAndGetVideo(data,m_cfg.m_cfg.ipcList.at(i).NVRChannel,m_ipcList.at(i)->m_lRealPlayHandle);
        }
    }
    else//不使用nvr
    {
        emit m_ipcList.at(i)->sigSnapAndGetVideo(data);
    }
#endif
}
